#include "HttpServer.h"
#include "HttpContext.h"
#include "HttpResponse.h"
#include "Spark/Utils/Logger.h"

static void defaultHttpCallback(const HttpRequest&, HttpResponse& resp)
{
    resp.setStatusCode(HttpStatusCode::kNotFound).setMessage("Not Found");
}

HttpServer::HttpServer(EventLoop* loop,
                       const SocketAddr& listenAddr,
                       const string& name)
    : server_(loop, listenAddr, name), httpCallback_(defaultHttpCallback)
{
    server_.setConnectionCallback([this](const TcpConnectionPtr& conn)
                                  { onConnection(conn); });
    server_.setRecvMessageCallback(
        [this](const TcpConnectionPtr& conn, MsgBuffer* buf, TimePoint tp)
        { onMessage(conn, buf, tp); });
}

void HttpServer::onRequest(const TcpConnectionPtr& conn, const HttpRequest& req)
{
    const string& connection = req.getHeader("Connection");
    HttpResponse response;
    bool close =
        connection == "close" || (req.getVersion() == Version::kHttp10 &&
                                  connection != "Keep-Alive");
    response.setClose(close);
    httpCallback_(req, response);
    MsgBuffer buf;
    response.appendToBuffer(&buf);
    conn->send(&buf);
    if (response)
    {
        conn->shutdown();
    }
}

void HttpServer::onMessage(const TcpConnectionPtr& conn,
                           MsgBuffer* buf,
                           TimePoint receiveTime)
{
    HttpContext* context =
        linb::any_cast<HttpContext>(conn->getMutableContext());

    if (!context->parseRequest(buf, receiveTime))
    {
        conn->send("HTTP/1.1 400 Bad Request\r\n\r\n");
        conn->shutdown();
    }

    if (context->gotAll())
    {
        onRequest(conn, context->request());
        context->reset();
    }
}

void HttpServer::onConnection(const TcpConnectionPtr& conn)
{
    if (conn->connected())
    {
        conn->setContext(HttpContext());
    }
}

void HttpServer::start()
{
    LOG_INFO << "HttpServer[" << server_.name() << "] starts listenning on "
             << server_.ipPort();
    server_.start();
}
